<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>OpenLayers with Geohash</title>
    <link rel="stylesheet" href="https://data.sunbt.ltd/lib/ol/v6.15.1/css/ol.css" type="text/css">
    <style>
        #map {
            width: 100%;
            height: 1000px;
        }
    </style>
</head>
<body>
<div id="map"></div>
<script src="https://data.sunbt.ltd/lib/ol/v6.15.1/build/ol.js"></script>
<script src="https://bundle.run/ngeohash@0.6.3"></script>
<script>
    // 初始化地图
    var map = new ol.Map({
        target: 'map', // 地图渲染的目标元素ID
        layers: [
            new ol.layer.Tile({
                source: new ol.source.OSM() // 使用OpenStreetMap作为底图
            })
        ],
        view: new ol.View({
            center: ol.proj.transform([119.4645, 32.9708], 'EPSG:4326', 'EPSG:3857'), // 设置初始中心点并转换投影坐标
            maxZoom: 18, // 最大缩放级别
            zoom: 8.2, // 初始缩放级别
            minZoom: 8 // 最小缩放级别
        })
    });

    // 根据缩放级别获取Geohash精度
    function getGeohashPrecision(zoomLevel) {
        console.log(zoomLevel) // 输出当前缩放级别
        if (zoomLevel <= 8) return 2;
        if (zoomLevel <= 9) return 3;
        if (zoomLevel <= 10) return 3;
        if (zoomLevel <= 11) return 4;
        if (zoomLevel <= 12) return 4;
        if (zoomLevel <= 13) return 4;
        if (zoomLevel <= 14) return 5;
        if (zoomLevel <= 15) return 5;
        if (zoomLevel <= 16) return 5;
        if (zoomLevel <= 17) return 6;
        if (zoomLevel <= 18) return 6;
        return 8;
    }

    // 获取地图范围内的Geohash矩形
    function getGeohashRectangles(extent, precision) {
        var minLat = extent[1]; // 地图范围的最小纬度
        var minLon = extent[0]; // 地图范围的最小经度
        var maxLat = extent[3]; // 地图范围的最大纬度
        var maxLon = extent[2]; // 地图范围的最大经度

        var geohashRectangles = [];

        // 获取左下角和右上角的Geohash编码
        var minGeohash = ngeohash.encode(minLat, minLon, precision);
        var maxGeohash = ngeohash.encode(maxLat, maxLon, precision);

        // 解码最小和最大Geohash的边界框
        var minBBox = ngeohash.decode_bbox(minGeohash);
        var maxBBox = ngeohash.decode_bbox(maxGeohash);

        // 计算步长
        var stepLat = (maxBBox[2] - minBBox[0]) / (Math.pow(2, precision));
        var stepLon = (maxBBox[3] - minBBox[1]) / (Math.pow(2, precision));

        // 生成连续的Geohash矩形
        for (var lat = minBBox[0]; lat <= maxBBox[2]; lat += stepLat) {
            for (var lon = minBBox[1]; lon <= maxBBox[3]; lon += stepLon) {
                var hash = ngeohash.encode(lat, lon, precision);
                var bbox = ngeohash.decode_bbox(hash);
                geohashRectangles.push({bbox: bbox, hash: hash});
            }
        }
        return geohashRectangles;
    }

    // 初始化矢量图层
    var vectorSource = new ol.source.Vector();
    var vectorLayer = new ol.layer.Vector({
        source: vectorSource,
        style: function (feature) {
            if (feature.getGeometry() instanceof ol.geom.Polygon) {
                return new ol.style.Style({
                    stroke: new ol.style.Stroke({
                        color: 'red',
                        width: 1
                    })
                });
            } else if (feature.getGeometry() instanceof ol.geom.Point) {
                return new ol.style.Style({
                    text: new ol.style.Text({
                        text: feature.get('name'), // 设置文本内容为Geohash值
                        scale: 1, // 缩放文本
                        fill: new ol.style.Fill({
                            color: 'black' // 文本颜色
                        }),
                        stroke: new ol.style.Stroke({
                            color: 'white', // 文本描边颜色
                            width: 2 // 描边宽度
                        })
                    })
                });
            }
        }
    });
    map.addLayer(vectorLayer); // 将矢量图层添加到地图

    // 绘制Geohash矩形和注记
    function drawGeohashRectangles(map, rectangles) {
        var features = rectangles.map(function (item) {
            var bbox = item.bbox; // Geohash矩形的边界框
            var hash = item.hash; // Geohash值
            var centerLon = (bbox[1] + bbox[3]) / 2; // 矩形中心的经度
            var centerLat = (bbox[0] + bbox[2]) / 2; // 矩形中心的纬度

            // 创建Polygon要素
            var polygonFeature = new ol.Feature({
                geometry: new ol.geom.Polygon([[
                    ol.proj.fromLonLat([bbox[1], bbox[0]]),
                    ol.proj.fromLonLat([bbox[3], bbox[0]]),
                    ol.proj.fromLonLat([bbox[3], bbox[2]]),
                    ol.proj.fromLonLat([bbox[1], bbox[2]]),
                    ol.proj.fromLonLat([bbox[1], bbox[0]])
                ]])
            });

            // 创建Point要素用于显示Geohash值（优化内存效率时得注释注记）
            var textFeature = new ol.Feature({
                geometry: new ol.geom.Point(ol.proj.fromLonLat([centerLon, centerLat])),
                name: hash
            });

            return [polygonFeature, textFeature]; // 返回多边形和文本要素
        }).flat();

        vectorSource.clear(); // 清除矢量源中的所有要素
        vectorSource.addFeatures(features); // 添加新的要素到矢量源
    }

    // 监听地图移动结束事件
    map.on('moveend', function () {
        var extent = map.getView().calculateExtent(map.getSize()); // 获取当前地图范围
        var zoomLevel = map.getView().getZoom(); // 获取当前缩放级别
        var precision = getGeohashPrecision(zoomLevel); // 根据缩放级别获取Geohash精度
        var geohashRectangles = getGeohashRectangles(ol.proj.transformExtent(extent, 'EPSG:3857', 'EPSG:4326'), precision); // 获取地图范围内的Geohash矩形
        drawGeohashRectangles(map, geohashRectangles); // 绘制Geohash矩形和注记
    });

    // 初始加载
    var initialExtent = map.getView().calculateExtent(map.getSize()); // 获取初始地图范围
    var initialZoomLevel = map.getView().getZoom(); // 获取初始缩放级别
    var initialPrecision = getGeohashPrecision(initialZoomLevel); // 根据初始缩放级别获取Geohash精度
    var initialGeohashRectangles = getGeohashRectangles(ol.proj.transformExtent(initialExtent, 'EPSG:3857', 'EPSG:4326'), initialPrecision); // 获取初始地图范围内的Geohash矩形
    drawGeohashRectangles(map, initialGeohashRectangles); // 绘制初始Geohash矩形和注记
</script>
</body>
</html>
